package com.qs.commontools.common.service;


import com.qs.commontools.utils.base.Base64StringTool;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

/**
 * Aes加解密处理层
 * @author qiusuo
 * @since 2021-10-14
 */
public class AesService {

    private static final String KEY_ALGORITHM = "AES";
    /**
     * 默认的加密算法 "算法/模式/补码方式"
     */
    private static final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";
    /**
     * 类初始化时，不初始化这个对象(延时加载，真正用的时候再创建)
     */
    private static AesService instance;

    private AesService() {
    }

    /**
     * 方法同步，调用效率低
     * @return
     */
    public static synchronized AesService getInstance(){
        if(instance == null){
            instance = new AesService();
        }
        return instance;
    }

    /**
     * todo ECB模式加密
     * @param content
     * @param key
     * @return
     */
    public String encryptECB(String content, String key) {
        try {
            // 创建密码器
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            // 初始化为加密模式的密码器
            // cipher.init(Cipher.ENCRYPT_MODE,getSecretKey(key))
            byte[] keyBytes = key.getBytes("UTF-8");
            SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
            // 加密
            byte[] contentBytes = content.getBytes("utf-8");
            byte[] result = cipher.doFinal(contentBytes);
            // 通过Base64转码返回
            return Base64StringTool.encodeBase64ToStr(result);
        } catch (Exception e) {
            e.printStackTrace();
            return  "加密失败:" + e.toString();
        }
    }

    /**
     * todo ECB模式解密
     * @param content
     * @param key
     * @return
     */
    public String decryptECB(String content, String key) {
        try {
            // 实例化
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            // 使用密钥初始化，设置为解密模式
            // cipher.init(Cipher.ENCRYPT_MODE,getSecretKey(key))
            byte[] keyBytes = key.getBytes("UTF-8");
            SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");
            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
            // 执行操作
            // 加密后的字节数组
            byte[] contentBytes = Base64StringTool.decodeBase64ToByte(content);
            // 解密后的字节数组
            byte[] resultBytes = cipher.doFinal(contentBytes);
            // 解密后的字节数组转为String
            return new String(resultBytes, "utf-8");
        } catch (Exception e) {
            e.printStackTrace();
            return  "解密失败:" + e.toString();
        }
    }

    /**
     * todo CBC模式加密
     * @param content
     * @param key
     * @return
     */
    public String encryptCBC(String content, String key, String iv) {
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            byte[] keyBytes = key.getBytes();
            SecretKeySpec skeySpec = new SecretKeySpec(keyBytes, "AES");
            // 使用CBC模式，需要一个向量iv，可增加加密算法的强度
            IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes("UTF-8"));
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivParameterSpec);
            byte[] contentBytes = content.getBytes("UTF-8");
            byte[] result = cipher.doFinal(contentBytes);
            return Base64StringTool.encodeBase64ToStr(result);
        } catch (Exception e) {
            e.printStackTrace();
            return "加密异常：" + e.toString();
        }
    }

    /**
     * todo CBC模式解密
     * @param content
     * @param key
     * @return
     */
    public String decryptCBC(String content, String key, String iv) {
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            IvParameterSpec zeroIv = new IvParameterSpec(iv.getBytes("UTF-8"));
            SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
            cipher.init(Cipher.DECRYPT_MODE, skeySpec, zeroIv);
            byte[] s = Base64StringTool.decodeBase64ToByte(content);
            byte[] result = cipher.doFinal(s);
            return new String(result, "UTF-8");
        } catch (Exception e) {
            e.printStackTrace();
            return "解密异常：" + e.toString();
        }
    }

    /**
     * todo 生成加密秘钥
     *
     * @return
     */
    private static SecretKeySpec getSecretKey(final String key) {
        //返回生成指定算法密钥生成器的 KeyGenerator 对象
        try {
            KeyGenerator kg = KeyGenerator.getInstance("AES");
            // 此类提供加密的强随机数生成器 (RNG)，该实现在windows上每次生成的key都相同，但是在部分linux或solaris系统上则不同。
            // SecureRandom random = new SecureRandom(key.getBytes());
            // 指定算法名称，不同的系统上生成的key是相同的。
//            SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
            SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
            random.setSeed(key.getBytes());
            //AES 要求密钥长度为 128
            kg.init(128, random);
            //生成一个密钥
            SecretKey secretKey = kg.generateKey();
            // 转换为AES专用密钥
            return new SecretKeySpec(secretKey.getEncoded(), "AES");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }
}
